#include"file.h"
#include"thread.h"
#include"debug.h"
#include"inode.h"
#include"stdio_kernal.h"
#include"dir.h"
#include"string.h"
#include"console.h"
#include"keyboard.h"

struct file file_table[MAX_OPEN_CNT];
extern struct dir root;
extern struct partition* curr_part;

static int32_t file_create(struct dir*,char*,oflag);
static int32_t open_file(int32_t,oflag);
static int32_t dir_create(struct dir* pdir,const char* dir_name);
static int32_t file_unlink(struct dir* pdir,char* filenam,int32_t i_no);
static int32_t get_file_idx(void);
static void task_fd_setup(int fd_idx,int fi_idx,struct winode* wi,oflag flag);
static int32_t fd2fi(int);
static uint32_t path_depth(const char* path);
static int32_t find_task_fd(void);
static bool isroot(const char* path);
static int32_t file_write(struct file* pf,void* src,uint32_t cnt);
static void wash_path(char* dest,char* src);


int sys_open(const char* path,oflag flag){
	if(path[strlen(path)-1]=='/'){
		printk("can't open a dir %s\n",path);
	}
	ASSERT(flag<=7);
	path_record pr;
	memset(&pr,0,sizeof(path_record));
	int32_t fd=-1;
	int32_t inode_no=path_parse(path,&pr);
	char* file_name=strrchr(pr.path,'/')+1;
	bool found=inode_no!=-1 ? true : false;	
	if(pr.ft==FT_DIR){
		printk("can't open a dir with open(),use opendir()\n");
		close_dir(pr.pdir);
		return fd;
	}
	
	if(!pr.is_path_valid){
		printk("can't access %s sub path %s is invalide\n",path,pr.path);
		close_dir(pr.pdir);
		return fd;
	}
	
	if(!found && !(flag & O_CREAT)){
		printk("path %s file %s not exist\n",pr.path,file_name);
		close_dir(pr.pdir);
		return fd;
	}else if(found && flag & O_CREAT){
		printk("%s has already exist\n",path);
		close_dir(pr.pdir);
		return fd;
	}
	
	switch(flag & O_CREAT){
		case O_CREAT:
			printk("create file\n");
			fd=file_create(pr.pdir,file_name,flag);
			break;
		case O_RDONLY:
		case O_WRONLY:
		case O_RDWR:
			//open file here
			fd=open_file(inode_no,flag);
			break;
	}
	close_dir(pr.pdir);
	return fd;
}

static int32_t fd2fi(int fd_idx){
	return current_thread()->fd_table[fd_idx];
}

void sys_close(int32_t fd){
	int fi_idx;
	fi_idx=fd2fi(fd);
	close_inode(file_table[fi_idx].inode);
	file_table[fi_idx].inode=NULL;
	current_thread()->fd_table[fd]=-1;
}

const char* sub_path(const char* path,char* name){
	const char* p=path;
	if(p[0]=='/'){
		while(*(++p)=='/');
	}
	while(*p!='/' && *p)
		*name++=*p++;
	*name=0;
	if(*p==0)
		p=NULL;
	return p;
}

//search current dir found entry with name e_name make pdir_e pointer to the entry if exist
//true entry exist otherwise false
static bool search_dir_entry(struct partition* part,struct dir* curr_dir,char* e_name,struct dir_entry* pdir_e){
	uint32_t all_blocks[NODE_ALL_BLOCK_CNT]={0};
	char* buffer;
	uint32_t b_idx=0;
	uint32_t e_idx=0;
	struct dir_entry* pentrys;

	buffer=sys_malloc(SECTOR_SIZE);
	ASSERT(buffer);
	node_all_block(part,&curr_dir->inode->pure_node,all_blocks,buffer);
	while(b_idx<BLOCK_INDEX_CNT_PER_SECTOR && all_blocks[b_idx]!=0){
		ide_read(part->disk,buffer,all_blocks[b_idx],1);
		pentrys=(struct dir_entry*)buffer;
		e_idx=0;
		while(e_idx<DIR_ENTRY_PER_SECTOR && pentrys[e_idx].name[0]){
			if(!strcmp(e_name,pentrys[e_idx].name)){
				memcpy(pdir_e,pentrys+e_idx,sizeof(struct dir_entry));
				return true;
			}
			e_idx++;
		}
		b_idx++;
	}
	sys_free(buffer);
	return false;
}

uint32_t path_depth(const char* path){
	uint32_t d=0;
	const char* p=path;
	ASSERT(p[0]=='/');
	do{
		while(*(++p)=='/');
		while(*p && *(++p)!='/');
		d++;
	}while(*p);
	return d;
}

static bool isroot(const char* path){
	if(!strcmp(path,"/") || !strcmp(path,"/.") || !strcmp(path,"/..")){
		return true;
	}
	return false;
}

int32_t path_parse(const char* path,path_record* pr){
	
	char name[FILE_NAME_MAX_LEN];
	const char* sub_p;
	struct dir* parent_dir;
	struct dir_entry dir_e;
	uint32_t parent_i_no=0;
	uint32_t pd=0;
	uint32_t rpd=0;
	
	if(isroot(path)){
		pr->pdir=&root;
		pr->ft=FT_DIR;
		pr->path[0]=0;
		pr->is_path_valid=true;
		return 0;
	}

	uint32_t p_len=strlen(path);
	ASSERT(path[0]=='/' && p_len>1 && p_len<MAX_PATH_LEN);
	pd=path_depth(path);
	parent_dir=&root;
	memset(name,0,FILE_NAME_MAX_LEN);
	pr->pdir=parent_dir;
	pr->ft=FT_UNKNOWN;
	pr->is_path_valid=false;
	sub_p=sub_path(path,name);
	rpd++;
	while(name[0]){
		ASSERT(strlen(pr->path)<512);
		strcat(pr->path,"/");
		strcat(pr->path,name);
		if(search_dir_entry(curr_part,parent_dir,name,&dir_e)){
			memset(name,0,FILE_NAME_MAX_LEN);
			if(sub_p){
				sub_p=sub_path(sub_p,name);
				rpd++;
			}
			if(dir_e.ft==FT_DIR){
				parent_i_no=parent_dir->inode->pure_node.i_no;
				close_dir(parent_dir);
				parent_dir=open_dir(curr_part,dir_e.i_no);
				pr->ft=FT_DIR;
				pr->pdir=parent_dir;
				continue;
			}else if(dir_e.ft==FT_NORMAL){
				pr->ft=FT_NORMAL;
				pr->is_path_valid=true;
				return dir_e.i_no;
			}
		}else{
			pr->is_path_valid=pd==rpd ? true : false;
			return -1;
		}
	}
	close_dir(parent_dir);
	pr->is_path_valid=true;
	pr->pdir=open_dir(curr_part,parent_i_no);
	return dir_e.i_no;
}

static int32_t file_create(struct dir* pdir,char* name,oflag flag){
	char* iobuffer;
	struct winode* wi;
	int32_t i_no;
	uint32_t error_state;
	struct dir_entry dir_e;
	uint32_t* page_dir_bak;
	task_tcb* curr;

	iobuffer=(char*)sys_malloc(SECTOR_SIZE);
	if(iobuffer==NULL){
		printk("no memory\n");
		return -1;
	}
	i_no=fs_bitmap_alloc(curr_part,FS_INODE_BITMAP);
	if(i_no<0){
		printk("partition file is full\n");
		return -1;
	}
	fs_bitmap_sync(curr_part,i_no,FS_INODE_BITMAP);
	wi=create_inode(curr_part,i_no); //malloc winode add to open_inode_list
	if(wi==NULL){
		printk("create inode failed\n");
		error_state=1;
		goto error_handle;
	}
	//insert dirctory entry in parent dirctory
	memset(&dir_e,0,sizeof(struct dir_entry));	
	create_dir_entry(name,wi->pure_node.i_no,FT_NORMAL,&dir_e);
	if(!dir_entry_insert_sync(curr_part,pdir,&dir_e,iobuffer)){
		printk("dir entry sync failed\n");
		error_state=2;
		goto error_handle;
	}
	inode_sync(curr_part,wi,iobuffer);
	int32_t fi_idx=get_file_idx();
	if(fi_idx==SYSTEM_MAX_FILE_OPEN_CNT){
		printk("os can't open more than %d files\n",SYSTEM_MAX_FILE_OPEN_CNT);
		return -1;
	}
	int32_t	fd_idx=find_task_fd();
	if(fd_idx==TASK_MAX_FD_CNT){
		error_state=3;
		goto error_handle;
	}
	task_fd_setup(fd_idx,fi_idx,wi,flag);
	sys_free(iobuffer);
	return fd_idx;

error_handle:
	switch(error_state){
		case 3:
			memset(&file_table[fi_idx],0,sizeof(struct file));
		case 2:
			list_remove(&wi->tag);
			curr=current_thread();
			page_dir_bak=curr->page_dir;
			curr->page_dir=NULL;
			sys_free(wi);
			curr->page_dir=page_dir_bak;
		case 1: 
			bitmap_set(&curr_part->inode_bitmap,i_no,0);
			break;
	}
	sys_free(iobuffer);
	return -1;
}

static void task_fd_setup(int fd_idx,int fi_idx,struct winode* wi,oflag flag){
	if(fd_idx>2 && fd_idx<8){
		file_table[fi_idx].inode=wi;
		file_table[fi_idx].fd_pos=0;
		file_table[fi_idx].f_flag=flag;
		current_thread()->fd_table[fd_idx]=fi_idx;
	}
}

static int32_t get_file_idx(void){
	int fi_idx;
	fi_idx=0;
	while(fi_idx<MAX_OPEN_CNT){
		if(!file_table[fi_idx].inode){
			return fi_idx;
		}
		fi_idx++;
	}
	return fi_idx;
}

static int32_t find_task_fd(void){
	task_tcb *curr;
	int fd_idx=3;
	curr=current_thread();
	while(curr->fd_table[fd_idx]!=-1){
		fd_idx++;
	}
	return fd_idx==TASK_MAX_FD_CNT ? -1 : fd_idx;
}

static int32_t open_file(int32_t i_no,oflag flag){
	int fd_idx=-1;
	int fi_idx=get_file_idx();
	if(fi_idx<MAX_OPEN_CNT){
		fd_idx=find_task_fd();
		struct winode* wi=open_inode(curr_part,i_no);
		task_fd_setup(fd_idx,fi_idx,wi,flag);
	}
	return fd_idx;
}

static int32_t file_unlink(struct dir* pdir,char* filename,int32_t i_no){
	char* iobuffer;	
	int32_t ret=0;

	iobuffer=sys_malloc(SECTOR_SIZE);
	if(!release_inode(curr_part,i_no,iobuffer)){
		ret=-1;
		goto fi;
	}
	
	if(!dir_entry_delete_sync(curr_part,pdir,filename,iobuffer)){
		ret=-1;
		goto fi;
	}
fi:
	sys_free(iobuffer);
	return ret;
}

static bool is_file_open(uint32_t i_no){
	uint32_t i=0;
	while(i<MAX_OPEN_CNT){
		if(i_no==file_table[i].inode->pure_node.i_no)
			return true;
		i++;
	}
	return false;
}

int32_t sys_unlink(const char* path){
	path_record pr;
	memset(&pr,0,sizeof(path_record));
	int32_t i_no;
	char* filename;
	int32_t ret;	

	i_no=path_parse(path,&pr);
	filename=strrchr(pr.path,'/')+1;

	if(i_no<0){
		printk("%s not found\n",filename);
		ret=-1;
		goto fi;
	}
	
	if(pr.ft==FT_DIR){
		printk("can't delete dircotry\n");
		ret=-1;
		goto fi;
	}
	
	if(!pr.is_path_valid){
		printk("%s path is invalid\n");
		ret=-1;
		goto fi;
	}

	//check file is open
	if(is_file_open(i_no)){
		printk("%s file is open\n",filename);
		ret=-1;
		goto fi;
	}
	
	if(file_unlink(pr.pdir,filename,i_no)<0){
		ret=-1;
		goto fi;
	}
	ret=0;
fi:
	close_dir(pr.pdir);
	return ret;
}

static int32_t dir_create(struct dir* pdir,const char* dir_name){
	char* iobuffer;
	struct winode* wi;
	int32_t i_no;
	int32_t b_no;
	uint32_t error_state;
	struct dir_entry dir_e;
	uint32_t* page_dir_bak;
	struct dir_entry* pentrys;
	task_tcb* curr;

	iobuffer=(char*)sys_malloc(SECTOR_SIZE);
	if(iobuffer==NULL){
		printk("no memory\n");
		return -1;
	}
	i_no=fs_bitmap_alloc(curr_part,FS_INODE_BITMAP);
	if(i_no<0){
		printk("partition file is full\n");
		return -1;
	}
	fs_bitmap_sync(curr_part,i_no,FS_INODE_BITMAP);
	wi=create_inode(curr_part,i_no); //malloc winode add to open_inode_list
	if(wi==NULL){
		printk("create inode failed\n");
		error_state=1;
		goto error_handle;
	}
	//insert dirctory entry in parent dirctory
	memset(&dir_e,0,sizeof(struct dir_entry));
	create_dir_entry(dir_name,wi->pure_node.i_no,FT_DIR,&dir_e);
	if(!dir_entry_insert_sync(curr_part,pdir,&dir_e,iobuffer)){
		printk("dir entry sync failed\n");
		error_state=2;
		goto error_handle;
	}
	b_no=fs_bitmap_alloc(curr_part,FS_BLOCK_BITMAP);
	if(b_no<0){
		printk("block is full\n");
		error_state=3;
		goto error_handle;
	}
	memset(iobuffer,0,SECTOR_SIZE);	
	pentrys=(struct dir_entry*)iobuffer;	
	fs_bitmap_sync(curr_part,b_no,FS_BLOCK_BITMAP);
	wi->pure_node.blocks[0]=b_no+curr_part->sb->free_block_lba;
	create_dir_entry(".",wi->pure_node.i_no,FT_DIR,&dir_e);
	memcpy(pentrys,&dir_e,sizeof(struct dir_entry));
	create_dir_entry("..",pdir->inode->pure_node.i_no,FT_DIR,&dir_e);
	memcpy(pentrys+1,&dir_e,sizeof(struct dir_entry));
	ide_write(curr_part->disk,iobuffer,wi->pure_node.blocks[0],1);	
	inode_sync(curr_part,wi,iobuffer);
	sys_free(iobuffer);	
	return 0;
error_handle:
	switch(error_state){
		case 3:
			dir_entry_delete_sync(curr_part,pdir,dir_name,iobuffer);
		case 2: 
			list_remove(&wi->tag);
			curr=current_thread();
			page_dir_bak=curr->page_dir;
			curr->page_dir=NULL;
			sys_free(wi);
			curr->page_dir=page_dir_bak;
		case 1: bitmap_set(&curr_part->inode_bitmap,i_no,0);
			break;
	}	
	sys_free(iobuffer);
	return -1;
}

int32_t sys_mkdir(const char* path){
	path_record pr;
	memset(&pr,0,sizeof(path_record));
	int32_t i_no;
	char* filename;
	int32_t ret;
	
	i_no=path_parse(path,&pr);
	filename=strrchr(pr.path,'/')+1;
	if(i_no!=-1){
		printk("%s dirctory or file is exist\n",filename);
		ret=-1;
		goto fi;
	}
	
	if(!pr.is_path_valid){
		printk("%s is invalid\n",pr.path);
		ret=-1;
		goto fi;
	}
	
	if(dir_create(pr.pdir,filename)<0){
		ret=-1;
		goto fi;
	}
	ret=0;
fi:
	close_dir(pr.pdir);
	return ret;
}

int32_t sys_stat(const char* path,struct stat* st){
	path_record pr;
	memset(&pr,0,sizeof(path_record));
	int32_t i_no;
	if(isroot(path)){
		st->i_no=curr_part->sb->root_ino;
		st->i_size=root.inode->pure_node.i_size;
		st->ft=FT_DIR;
		return 0;
	}
	i_no=path_parse(path,&pr);
	if(i_no<0){
		return -1;	
	}else{
		struct winode* wi;
		wi=open_inode(curr_part,i_no);
		st->i_no=i_no;
		st->i_size=wi->pure_node.i_size;
		st->ft=pr.ft;
	}
	close_dir(pr.pdir);
	return 0;
}

static bool file_reach_limit(struct file* pf,uint32_t cnt){
	return pf->inode->pure_node.i_size+cnt > FILE_SIZE_LIMIT ? true : false;
}

static bool alloc_write_block(struct partition* part,struct file* pf,uint32_t* w_block,uint32_t write_cnt,uint32_t* b_start_idx,uint32_t* block_cnt,char* iobuffer){
	
	uint32_t used_block_cnt;
	uint32_t final_block_cnt;
	uint32_t add_block_cnt;
	int32_t lba_idx;
	uint32_t b_idx;
	uint32_t end_b_idx;
	bool first_write_block_has_old_data;

	used_block_cnt=div_round_up(pf->inode->pure_node.i_size,BLOCK_SIZE);
	final_block_cnt=div_round_up(pf->inode->pure_node.i_size+write_cnt,BLOCK_SIZE);
	add_block_cnt=final_block_cnt-used_block_cnt;
	first_write_block_has_old_data= pf->inode->pure_node.i_size%BLOCK_SIZE==0 ? false : true;
	*b_start_idx=pf->inode->pure_node.i_size/BLOCK_SIZE;
	*block_cnt=add_block_cnt;
	b_idx=*b_start_idx;
	end_b_idx=*b_start_idx+*block_cnt;

	if(add_block_cnt==0){
		if(final_block_cnt<=INODE_BLOCK_CNT-1){
			if(!pf->inode->pure_node.blocks[*b_start_idx]){
				lba_idx=fs_bitmap_alloc(part,FS_BLOCK_BITMAP);
				if(lba_idx<0){
					return false;
				}
				fs_bitmap_sync(part,lba_idx,FS_BLOCK_BITMAP);
				pf->inode->pure_node.blocks[*b_start_idx]=part->sb->free_block_lba+lba_idx;
			}
			w_block[*b_start_idx]=pf->inode->pure_node.blocks[*b_start_idx];
		}else{
			ASSERT(pf->inode->pure_node.blocks[INODE_BLOCK_CNT-1]);
			ide_read(part->disk,(char*)(w_block+INODE_BLOCK_CNT-1),pf->inode->pure_node.blocks[INODE_BLOCK_CNT-1],1);
		}
		if(first_write_block_has_old_data){
			ide_read(part->disk,iobuffer,w_block[*b_start_idx],1);
		}
	}else{
		if(final_block_cnt<=INODE_BLOCK_CNT-1){
			while(b_idx<end_b_idx){
				lba_idx=fs_bitmap_alloc(part,FS_BLOCK_BITMAP);
				if(lba_idx<0){
					return false;
				}
				fs_bitmap_sync(part,lba_idx,FS_BLOCK_BITMAP);
				w_block[b_idx]=part->sb->free_block_lba+lba_idx;
				pf->inode->pure_node.blocks[b_idx]=w_block[b_idx];
				b_idx++;
			}
		}else if(used_block_cnt<=INODE_BLOCK_CNT-1 && final_block_cnt>INODE_BLOCK_CNT-1){
			while(b_idx<INODE_BLOCK_CNT-1){
				w_block[b_idx]=pf->inode->pure_node.blocks[b_idx];
				b_idx++;
			}
			lba_idx=fs_bitmap_alloc(part,FS_BLOCK_BITMAP);		
			if(lba_idx<0){
				return false;
			}
			pf->inode->pure_node.blocks[INODE_BLOCK_CNT-1]=part->sb->free_block_lba+lba_idx;
			while(b_idx<end_b_idx){
				lba_idx=fs_bitmap_alloc(part,FS_BLOCK_BITMAP);
				if(lba_idx<0){
					return false;
				}
				fs_bitmap_sync(part,lba_idx,FS_BLOCK_BITMAP);
				w_block[b_idx]=part->sb->free_block_lba+lba_idx;
				pf->inode->pure_node.blocks[b_idx]=w_block[b_idx];
				b_idx++;
			}
		}else if(final_block_cnt>INODE_BLOCK_CNT-1){
			while(b_idx<end_b_idx){
				lba_idx=fs_bitmap_alloc(part,FS_BLOCK_BITMAP);
				if(lba_idx<0){
					return false;
				}
				fs_bitmap_sync(part,lba_idx,FS_BLOCK_BITMAP);
				w_block[b_idx]=part->sb->free_block_lba+lba_idx;
				pf->inode->pure_node.blocks[b_idx]=w_block[b_idx];
				b_idx++;
			}
		}
		if(first_write_block_has_old_data){
			ide_read(part->disk,iobuffer,w_block[*b_start_idx],1);
		}
	}
	return true;
}

static int32_t file_write(struct file* pf,void* src,uint32_t cnt){
	uint32_t w_block[NODE_ALL_BLOCK_CNT]={0};
	uint32_t b_idx;
	uint32_t b_start_idx;
	uint32_t write_block_cnt;
	uint32_t size_left;
	uint32_t chunk_size;
	uint32_t s_off,s_left;
	uint32_t write_bytes=0;
	char iobuffer[BLOCK_SIZE]={0};
	
	if(file_reach_limit(pf,cnt)){
		return -1;
	}
	
	if(!alloc_write_block(curr_part,pf,w_block,cnt,&b_start_idx,&write_block_cnt,iobuffer)){
		return -1;
	}
	size_left=cnt;
	b_idx=b_start_idx;
	while(write_bytes<cnt){
		s_off=pf->inode->pure_node.i_size%512;
		s_left=BLOCK_SIZE-s_off;
		chunk_size=size_left<s_left ? size_left : s_left;
		//last write block we should clear the iobuffer
		if(write_block_cnt>1 && b_idx==b_start_idx+write_block_cnt-1){
			memset(iobuffer,0,BLOCK_SIZE);
		}
		memcpy(iobuffer+s_off,src+write_bytes,chunk_size);
		ide_write(curr_part->disk,iobuffer,w_block[b_idx],1);
		pf->fd_pos+=chunk_size;
		pf->inode->pure_node.i_size+=chunk_size;
		size_left-=chunk_size;
		write_bytes+=chunk_size;
		b_idx++;
	}
	inode_sync(curr_part,pf->inode,iobuffer);
	return cnt;
}

static bool is_valide_fd(int32_t fd){
	if(fd<0 || fd>TASK_MAX_FD_CNT-1)
		return false;
	if(current_thread()->fd_table[fd]==-1){
		return false;
	}
	return true;
}

int32_t sys_write(int32_t fd,void* src,uint32_t cnt){
	struct file* pf;
	
	if(!is_valide_fd(fd)){
		return -1;
	}

	if(fd==stdout_no){
		console_put_str(src);
		return cnt;
	}
	
	pf=file_table+fd2fi(fd);
	if(pf->f_flag & O_WRONLY || pf->f_flag & O_RDWR){
		return file_write(pf,src,cnt);
	}else{
		printk("file can not write\n");
		return -1;
	}
	return -1;
}

static void fill_file_read_block(struct partition* part,struct file* pf,uint32_t* r_block,uint32_t read_cnt,uint32_t* b_start_idx,uint32_t* b_end_idx){
	uint32_t read_block_start;
	uint32_t read_block_end;
	uint32_t b_idx;

	ASSERT(read_cnt>0);
	read_block_start=pf->fd_pos/BLOCK_SIZE;
	read_block_end=div_round_up(pf->fd_pos+read_cnt,BLOCK_SIZE);

	if(read_block_end<=INODE_BLOCK_CNT-1){
		b_idx=read_block_start;
		while(b_idx<INODE_BLOCK_CNT-1){
			r_block[b_idx]=pf->inode->pure_node.blocks[b_idx];
			b_idx++;
		}
	}else{
		b_idx=read_block_start;
		while(b_idx<INODE_BLOCK_CNT-1){
			r_block[b_idx]=pf->inode->pure_node.blocks[b_idx];
			b_idx++;
		}
		ide_read(part->disk,(char*)(r_block+INODE_BLOCK_CNT-1),pf->inode->pure_node.blocks[INODE_BLOCK_CNT-1],1);
	}
	*b_start_idx=read_block_start;
	*b_end_idx=read_block_end;
}

static int32_t file_read(struct file* pf,void* buffer,uint32_t cnt){
	uint32_t r_block[NODE_ALL_BLOCK_CNT]={0};
	uint32_t size=cnt;
	uint32_t b_idx;
	uint32_t b_start_idx;
	uint32_t b_end_idx;
	uint32_t size_left;
	uint32_t chunk_size;
	uint32_t s_off,s_left;
	uint32_t read_bytes=0;
	char iobuffer[BLOCK_SIZE]={0};

	if(pf->fd_pos+cnt > pf->inode->pure_node.i_size){
		size=pf->inode->pure_node.i_size-pf->fd_pos;
		if(size==0){
			return -1;
		}
	}

	fill_file_read_block(curr_part,pf,r_block,size,&b_start_idx,&b_end_idx);
	size_left=size;
	b_idx=b_start_idx;
	while(b_idx<b_end_idx){
		s_off=pf->fd_pos%BLOCK_SIZE;
		s_left=BLOCK_SIZE-s_off;
		chunk_size= size_left<s_left ? size_left : s_left;
		ide_read(curr_part->disk,iobuffer,r_block[b_idx],1);
		memcpy(buffer+read_bytes,iobuffer+s_off,chunk_size);
		pf->fd_pos+=chunk_size;
		size_left-=chunk_size;
		read_bytes+=chunk_size;
		b_idx++;
	}
	ASSERT(read_bytes==size);
	return size;
}

int32_t sys_read(int32_t fd,void* buffer,uint32_t cnt){
	struct file* pf;
	if(!is_valide_fd(fd)){
		return -1;
	}
	if(fd==stdin_no){
		//read data from keyboard
		return keyboard_read(buffer,cnt);
	}else{
		pf=file_table+fd2fi(fd);
		if(pf->f_flag==O_RDONLY || pf->f_flag & O_RDWR){
			return file_read(pf,buffer,cnt);
		}else{
			printk("write only file\n");
			return -1;
		}
	}
	return -1;
}

int32_t sys_lseek(int32_t fd,int32_t offset,uint8_t whence){
	int32_t off;
	struct file* pf;
	if(!is_valide_fd(fd)){
		return -1;
	}

	if(whence>3){
		return -1;
	}

	pf=file_table+fd2fi(fd);

	if(whence==SEEK_SET){
		off=offset;
	}else if(whence==SEEK_CUR){
		off=pf->fd_pos+offset;
	}else if(whence==SEEK_END){
		off=pf->inode->pure_node.i_size+offset;
	}

	if(off<0 || (uint32_t)off>pf->inode->pure_node.i_size-1){
		return -1;
	}
	pf->fd_pos=off;
	return off;
}

static void wash_path(char* dest,char* src){
	char name[MAX_PATH_LEN];	
	const char* sub;
	char* slash_ptr;
	
	sub=sub_path(src,name);
	if(!name[0]){
		dest[0]='/';
		dest[1]=0;
		return;
	}
	dest[0]='/';
	dest[1]=0;
	while(name[0]){
		if(!strcmp("..",name)){
			slash_ptr=strrchr(dest,'/');
			if(slash_ptr==dest){
				*(slash_ptr+1)=0;
			}else{
				*(slash_ptr)=0;
			}
		}else if(strcmp(".",name)){
			if(strcmp("/",dest)){
				strcat(dest,"/");
			}
			strcat(dest,name);
		}
		name[0]=0;
		if(sub)
			sub=sub_path(sub,name);
	}
}

void abs_path(char* dest,char* src){
	char buffer[MAX_PATH_LEN]={0};
	if(src[0]=='/'){
		wash_path(dest,src);
	}else{
		getcwd(buffer,MAX_PATH_LEN);
		if(!(buffer[0]=='/' && buffer[1]==0)){
			strcat(buffer,"/");
		}
		strcat(buffer,src);
		wash_path(dest,buffer);
	}
}
